#include <stdio.h>
#include <pthread.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <string.h>

#define A 1664525
#define C 1013904223

#define NUM_THREADS 12

#define IT 100000000

int inside[NUM_THREADS];

unsigned int next(unsigned int x) {
        return x*A+C;
}

// função para thread 
void *throw_dart(void *t) {
	
	int id = (int) t;
	int i;
	int x, y;
	float _x, _y;
	y = next(time(NULL));
	for(i=0; i<(IT/NUM_THREADS); ++i) {
		x = next(y);
		y = next(x);
		_x = (float) x/INT_MAX;
		_y = (float) y/INT_MAX;	

		if (sqrt(_x*_x + _y*_y) <= 1) inside[id]++;
	}
	pthread_exit((void *) t);
}



int main(int argc, char *argv[]) {

	// verefica a entrada do programa
	if (argc != 2) {
		printf("usage: ./monte_carlo (0 = sequential / 1 = parallel)\n");
		return 0;
	}

	int sum_inside = 0;
	
	// INICIO DO PROCESSO EM SEQUENCIAL
	if (argv[1][0] == '0') {

		int i;
		int x, y;
		float _x, _y;

		y = next(time(NULL));
		
		// realiza o calculo e realiza a somatória
		for(i=0; i<IT; ++i) {
			x = next(y);
			y = next(x);
			_x = (float) x/INT_MAX;
			_y = (float) y/INT_MAX;	

			if (sqrt(_x*_x + _y*_y) <= 1) sum_inside++;
		}


	// INICIO DO PROCESSO EM PARALELO
	} else {

		// aloca memoria
		memset(inside, NUM_THREADS * sizeof(int), 0);
		
		// cria um vetor de thread
		pthread_t thread[NUM_THREADS];
		
		int t;
		int ret;
		void *status;

		// inicializa as threads
		for(t=0; t<NUM_THREADS; ++t) {
			ret = pthread_create( &thread[t], NULL, throw_dart, (void *)t);
			if (ret) {
				printf("ERRO; return pthread_create(): %d\n", ret);
         			return -1;
			}
		}

		// espera todas as threads finalizarem para continuar o programa
		for(t=0; t<NUM_THREADS; ++t) {
			ret = pthread_join(thread[t], &status);
			if (ret) {
				printf("ERRO; return pthread_join() : %d\n", ret);
				return -1;
			}
			// realiza o somatório de todas os resultados das threads
			sum_inside += inside[t];
		}
	}
		
	// calcula o valor final de PI
	float PI = (float) (4*sum_inside)	/ IT;
	printf("\n\n   PI = %.6f\n\n", PI);

	return 0;
}
